package chart;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.geom.Rectangle2D;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.CustomXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.chart.ChartColor;

import util.Constants;
import util.PropertiesLoader;

import db.DatabaseManager;
import db.entity.StationStatus;
import db.entity.StationStatusPK;

public class LiveDataChartPanel  extends ChartPanel{

	private static final Logger log = Logger.getLogger(LiveDataChartPanel.class);
	private static final long serialVersionUID = 1L;
	
	private TimeSeries tempSeries = null;
	private TimeSeries vacuumLSeries = null;
	private TimeSeries vacuumRSeries = null;
	private TimeSeries pressureLSeries = null;
	private TimeSeries pressureRSeries = null;
	
	private TimeSeriesCollection tempDataset = null;
	private TimeSeriesCollection vacuumLDataset = null;
	private TimeSeriesCollection vacuumRDataset = null;
	private TimeSeriesCollection pressureLDataset = null;
	private TimeSeriesCollection pressureRDataset = null;
	
	private NumberAxis temperatureAxis = null;
	private NumberAxis vacuumLAxis = null;
	private NumberAxis vacuumRAxis = null;
	private NumberAxis pressureLAxis = null;
	private NumberAxis pressureRAxis = null;
	private DateAxis dateAxis = null;
	
	private ArrayList<String> tmpToolTipArray = null;
	private ArrayList<String> vcLToolTipArray = null;
	private ArrayList<String> vcRToolTipArray = null;
	private ArrayList<String> prLToolTipArray = null;
	private ArrayList<String> prRToolTipArray = null;
	
	private XYPlot liveDataPlot = null;
	private JFreeChart liveDataChart = null;

	private String currentStationNumber = "";
	private DatabaseManager dm = null;
	private LiveDataThread liveDataThread = null;
	
	private static final long REFRESH_INTERVAL = 30000;
	
	public LiveDataChartPanel()
	{
		super(null);
		initSeries();
		initAxis();
		initToolTipArray();
		renderPlot();
		liveDataChart = new JFreeChart("Live Data Monitor", JFreeChart.DEFAULT_TITLE_FONT, liveDataPlot, true);
		liveDataChart.setBorderVisible(true);
		this.setChart(liveDataChart);
		this.setSize(800, 600);
		this.setDisplayToolTips(true);
	}
	
	private void initSeries()
	{
		tempSeries = new TimeSeries("Temperature");		
		tempSeries.setMaximumItemCount(20);
		tempDataset = new TimeSeriesCollection();
		tempDataset.addSeries(tempSeries);
		
		vacuumLSeries = new TimeSeries("Vacuum L");	
		vacuumLSeries.setMaximumItemCount(20);
		vacuumLDataset = new TimeSeriesCollection();
		vacuumLDataset.addSeries(vacuumLSeries);

		vacuumRSeries = new TimeSeries("Vacuum R");	
		vacuumRSeries.setMaximumItemCount(20);
		vacuumRDataset = new TimeSeriesCollection();	
		vacuumRDataset.addSeries(vacuumRSeries);

		pressureLSeries = new TimeSeries("Pressure L");	
		pressureLSeries.setMaximumItemCount(20);
		pressureLDataset = new TimeSeriesCollection();		
		pressureLDataset.addSeries(pressureLSeries);

		pressureRSeries = new TimeSeries("Pressure R");	
		pressureRSeries.setMaximumItemCount(20);
		pressureRDataset = new TimeSeriesCollection();		
		pressureRDataset.addSeries(pressureRSeries);
	}
	
	private void initAxis()
	{
		temperatureAxis = new NumberAxis("Temperature (Celsius)");
		temperatureAxis.setLabelPaint(Color.red);
		temperatureAxis.setNumberFormatOverride(Constants.GRAPH_DF);
		temperatureAxis.setAutoRangeIncludesZero(false);
		
		vacuumLAxis = new NumberAxis("Vacuum L (Inches Hg)");
		vacuumLAxis.setLabelPaint(Color.blue);
		vacuumLAxis.setNumberFormatOverride(Constants.GRAPH_DF);
		vacuumLAxis.setAutoRangeIncludesZero(false);
		
		vacuumRAxis = new NumberAxis("Vacuum R (Inches Hg)");
		vacuumRAxis.setLabelPaint(Constants.ORANGE);
		vacuumRAxis.setNumberFormatOverride(Constants.GRAPH_DF);
		vacuumRAxis.setAutoRangeIncludesZero(false);
		
		pressureLAxis = new NumberAxis("Pressure L (PSI)");
		pressureLAxis.setLabelPaint(Constants.DARK_GREEN);
		pressureLAxis.setNumberFormatOverride(Constants.GRAPH_DF);
		pressureLAxis.setAutoRangeIncludesZero(false);
		
		pressureRAxis = new NumberAxis("Pressure R (PSI)");
		pressureRAxis.setLabelPaint(Color.cyan);
		pressureRAxis.setNumberFormatOverride(Constants.GRAPH_DF);
		pressureRAxis.setAutoRangeIncludesZero(false);
		
		dateAxis = new DateAxis();
		dateAxis.setDateFormatOverride(new SimpleDateFormat("dd,MMM,yyyy(hh:mm:ssa)"));
	}
		
	private void initToolTipArray()
	{
		tmpToolTipArray = new ArrayList<String>();
		vcLToolTipArray = new  ArrayList<String>();
		vcRToolTipArray = new  ArrayList<String>();
		prLToolTipArray = new  ArrayList<String>();
		prRToolTipArray = new  ArrayList<String>();
	}
	
	
	private void renderPlot()
	{
		liveDataPlot = new XYPlot();
		
		//Temperature
		XYLineAndShapeRenderer tempRenderer = new XYLineAndShapeRenderer();
		tempRenderer.setBaseShape(new Rectangle2D.Float(-2.0F, -2.0F, 4.0F, 4.0F));
		tempRenderer.setBasePaint(Color.red);
		tempRenderer.setUseFillPaint(true);
		tempRenderer.setBaseFillPaint(Color.LIGHT_GRAY);
		tempRenderer.setBaseShapesVisible(true);
		tempRenderer.setBaseShapesFilled(true);
		tempRenderer.setSeriesPaint(0, Color.red);
		CustomXYToolTipGenerator tmpToolTipGenerator = new CustomXYToolTipGenerator();
		tmpToolTipGenerator.addToolTipSeries(this.tmpToolTipArray);
		// XYToolTipGenerator tempToolTipGenerator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", DateFormat.getDateTimeInstance(), NumberFormat.getInstance());
		tempRenderer.setBaseToolTipGenerator(tmpToolTipGenerator);
				
		liveDataPlot.setRangeAxis(0, temperatureAxis);
		liveDataPlot.setRangeAxisLocation(0, AxisLocation.BOTTOM_OR_LEFT);
		liveDataPlot.setDataset(0, tempDataset);
		liveDataPlot.mapDatasetToRangeAxis(0, 0);
		liveDataPlot.setRenderer(0, tempRenderer);
	
		//Vacuum Left
		XYLineAndShapeRenderer vacLRenderer = new XYLineAndShapeRenderer();
		vacLRenderer.setBaseShape(new Rectangle2D.Float(-2.0F, -2.0F, 4.0F, 4.0F));
		vacLRenderer.setBasePaint(Color.blue);
		vacLRenderer.setUseFillPaint(true);
		vacLRenderer.setBaseFillPaint(Color.LIGHT_GRAY);
		vacLRenderer.setBaseShapesVisible(true);
		vacLRenderer.setBaseShapesFilled(true);
		vacLRenderer.setSeriesPaint(0, Color.blue);
		CustomXYToolTipGenerator vcLToolTipGenerator = new CustomXYToolTipGenerator();
		vcLToolTipGenerator.addToolTipSeries(this.vcLToolTipArray);
		//XYToolTipGenerator vacLToolTipGenerator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", DateFormat.getDateTimeInstance(), NumberFormat.getInstance());
		vacLRenderer.setBaseToolTipGenerator(vcLToolTipGenerator);
					
		liveDataPlot.setRangeAxis(1, vacuumLAxis);
		liveDataPlot.setRangeAxisLocation(1, AxisLocation.TOP_OR_LEFT);
		liveDataPlot.setDataset(1, vacuumLDataset);
		liveDataPlot.mapDatasetToRangeAxis(1, 1);
		liveDataPlot.setRenderer(1, vacLRenderer);
	
		//Vacuum Right
		XYLineAndShapeRenderer vacRRenderer = new XYLineAndShapeRenderer();
		vacRRenderer.setBaseShape(new Rectangle2D.Float(-2.0F, -2.0F, 4.0F, 4.0F));
		vacRRenderer.setBasePaint(Constants.ORANGE);
		vacRRenderer.setUseFillPaint(true);
		vacRRenderer.setBaseFillPaint(Color.LIGHT_GRAY);
		vacRRenderer.setBaseShapesVisible(true);
		vacRRenderer.setBaseShapesFilled(true);
		vacRRenderer.setSeriesPaint(0, Constants.ORANGE);
		CustomXYToolTipGenerator vcRToolTipGenerator = new CustomXYToolTipGenerator();
		vcRToolTipGenerator.addToolTipSeries(this.vcRToolTipArray);
		//XYToolTipGenerator vacRToolTipGenerator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", DateFormat.getDateTimeInstance(), NumberFormat.getInstance());
		vacRRenderer.setBaseToolTipGenerator(vcRToolTipGenerator);
		
		liveDataPlot.setRangeAxis(2, vacuumRAxis);
		liveDataPlot.setRangeAxisLocation(2, AxisLocation.TOP_OR_LEFT);
		liveDataPlot.setDataset(2, vacuumRDataset);
		liveDataPlot.mapDatasetToRangeAxis(2, 2);
		liveDataPlot.setRenderer(2, vacRRenderer);
	
		//Pressure Left
		XYLineAndShapeRenderer prsLRenderer = new XYLineAndShapeRenderer();
		prsLRenderer.setBaseShape(new Rectangle2D.Float(-2.0F, -2.0F, 4.0F, 4.0F));
		prsLRenderer.setBasePaint(Constants.DARK_GREEN);
		prsLRenderer.setUseFillPaint(true);
		prsLRenderer.setBaseFillPaint(Color.LIGHT_GRAY);
		prsLRenderer.setBaseShapesVisible(true);
		prsLRenderer.setBaseShapesFilled(true);
		prsLRenderer.setSeriesPaint(0, Constants.DARK_GREEN);
		CustomXYToolTipGenerator prLToolTipGenerator = new CustomXYToolTipGenerator();
		prLToolTipGenerator.addToolTipSeries(this.prLToolTipArray);
		//XYToolTipGenerator prsLToolTipGenerator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", DateFormat.getDateTimeInstance(), NumberFormat.getInstance());
		prsLRenderer.setBaseToolTipGenerator(prLToolTipGenerator);
		
		liveDataPlot.setRangeAxis(3, pressureLAxis);
		liveDataPlot.setRangeAxisLocation(3, AxisLocation.BOTTOM_OR_RIGHT);
		liveDataPlot.setDataset(3, pressureLDataset);
		liveDataPlot.mapDatasetToRangeAxis(3, 3);
		liveDataPlot.setRenderer(3, prsLRenderer);
	
		//Pressure Right
		XYLineAndShapeRenderer prsRRenderer = new XYLineAndShapeRenderer();
		prsRRenderer.setBaseShape(new Rectangle2D.Float(-2.0F, -2.0F, 4.0F, 4.0F));
		prsRRenderer.setBasePaint(Color.cyan);
		prsRRenderer.setUseFillPaint(true);
		prsRRenderer.setBaseFillPaint(Color.LIGHT_GRAY);
		prsRRenderer.setBaseShapesVisible(true);
		prsRRenderer.setBaseShapesFilled(true);
		prsRRenderer.setSeriesPaint(0, Color.cyan);
		CustomXYToolTipGenerator prRToolTipGenerator = new CustomXYToolTipGenerator();
		prRToolTipGenerator.addToolTipSeries(this.prRToolTipArray);
		//XYToolTipGenerator prsRToolTipGenerator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", DateFormat.getDateTimeInstance(), NumberFormat.getInstance());
		prsRRenderer.setBaseToolTipGenerator(prRToolTipGenerator);
		
		liveDataPlot.setRangeAxis(4, pressureRAxis);
		liveDataPlot.setRangeAxisLocation(4, AxisLocation.BOTTOM_OR_RIGHT);
		liveDataPlot.setDataset(4, pressureRDataset);
		liveDataPlot.mapDatasetToRangeAxis(4, 4);
		liveDataPlot.setRenderer(4, prsRRenderer);
		
		// Add Horizontal Axis
		liveDataPlot.setDomainAxis(dateAxis);
	}

	
	public void resetStation(String stationNo)
	{
		currentStationNumber = stationNo;
		StationStatus sItem = retrieveStationStatus();
		resetStation(sItem);
		if(liveDataThread==null)
		{
			liveDataThread = new LiveDataThread();
			liveDataThread.start();
		}
	}

	public void stopLiveData()
	{
		liveDataThread.stopThread();
		liveDataThread = null;
	}
	
	private StationStatus retrieveStationStatus()
	{
		if(dm==null)
			dm = new db.DatabaseManager();
		
		List<StationStatus> sList = dm.listStationStatusByStationName(currentStationNumber);
		
		if(sList==null || sList.size()==0)
			return null;
		
		StationStatus sItem = sList.get(0);
		Calendar cd = Calendar.getInstance();
		cd.add(Calendar.MILLISECOND, -PropertiesLoader.getRegisterReaderInterval());
		Date prevReadTime = cd.getTime();
		System.out.println(prevReadTime);
		// If the no new data since Previous Read, return no updates
		if(prevReadTime.after(sItem.getId().getDataDate()))
			return null;
		
		return sItem;
	}
	
	private void resetStation(StationStatus statusItem)
	{
		tempDataset.removeAllSeries();
		vacuumLDataset.removeAllSeries();
		vacuumRDataset.removeAllSeries();
		pressureLDataset.removeAllSeries();
		pressureRDataset.removeAllSeries();
				
		tempSeries.clear();
		vacuumLSeries.clear();
		vacuumRSeries.clear();
		pressureLSeries.clear();
		pressureRSeries.clear();
		
		tmpToolTipArray.clear();
		vcLToolTipArray.clear();
		vcRToolTipArray.clear();
		prLToolTipArray.clear();
		prRToolTipArray.clear();
/*		
		tempSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getTemperature());
		tmpToolTipArray.add("Temperature: "+Constants.GRAPH_DF.format(statusItem.getTemperature())+" ("+statusItem.getCurrentPadId()+" @ "+statusItem.getId().getDataDate()+")");
	
		vacuumLSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getVacuumLeft());
		vcLToolTipArray.add("Vacuum Left: "+Constants.GRAPH_DF.format(statusItem.getVacuumLeft())+" ("+statusItem.getCurrentPadId()+" @ "+statusItem.getId().getDataDate()+")");
	
		vacuumRSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getVacuumRight());
		vcRToolTipArray.add("Vacuum Right: "+Constants.GRAPH_DF.format(statusItem.getVacuumRight())+" ("+statusItem.getCurrentPadId()+" @ "+statusItem.getId().getDataDate()+")");
	
		pressureLSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getPressureLeft());
		prLToolTipArray.add("Pressure Left: "+Constants.GRAPH_DF.format(statusItem.getPressureLeft())+" ("+statusItem.getCurrentPadId()+" @ "+statusItem.getId().getDataDate()+")");
	
		pressureRSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getPressureRight());
		prRToolTipArray.add("Pressure Right: "+Constants.GRAPH_DF.format(statusItem.getPressureRight())+" ("+statusItem.getCurrentPadId()+" @ "+statusItem.getId().getDataDate()+")");
*/
		
		tempDataset.addSeries(tempSeries);	

		vacuumLDataset.addSeries(vacuumLSeries);
		
		vacuumRDataset.addSeries(vacuumRSeries);
		
		pressureLDataset.addSeries(pressureLSeries);
	
		pressureRDataset.addSeries(pressureRSeries);
	}
	
	private void loadData(StationStatus statusItem)
	{
		tempSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getTemperature());
		tmpToolTipArray.add("Temperature: "+Constants.GRAPH_DF.format(statusItem.getTemperature())+" ("+statusItem.getId().getDataDate()+")");
	
		vacuumLSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getVacuumLeft());
		vcLToolTipArray.add("Vacuum Left: "+Constants.GRAPH_DF.format(statusItem.getVacuumLeft())+" ("+statusItem.getId().getDataDate()+")");
	
		vacuumRSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getVacuumRight());
		vcRToolTipArray.add("Vacuum Right: "+Constants.GRAPH_DF.format(statusItem.getVacuumRight())+" ("+statusItem.getId().getDataDate()+")");
	
		pressureLSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getPressureLeft());
		prLToolTipArray.add("Pressure Left: "+Constants.GRAPH_DF.format(statusItem.getPressureLeft())+" ("+statusItem.getId().getDataDate()+")");
	
		pressureRSeries.addOrUpdate(new Millisecond(statusItem.getId().getDataDate()), statusItem.getPressureRight());
		prRToolTipArray.add("Pressure Right: "+Constants.GRAPH_DF.format(statusItem.getPressureRight())+" ("+statusItem.getId().getDataDate()+")");

		this.restoreAutoBounds();
	}
	
	private class LiveDataThread extends Thread
	{		
		boolean flag = true;
		public void run()
		{
			try 
			{
				while(flag)
				{				
					StationStatus sItem = retrieveStationStatus();
					if(sItem != null)
						loadData(sItem);
					else
					{
						System.out.println("--Load blank item");
						sItem = new StationStatus();
						sItem.setPressureLeft(0.0F);
						sItem.setPressureRight(0.0F);
						sItem.setTemperature(0.0F);
						sItem.setVacuumLeft(0.0F);
						sItem.setVacuumRight(0.0F);
						
						StationStatusPK ssID = new StationStatusPK();
						ssID.setDataDate(new Date());
						
						sItem.setId(ssID);
						loadData(sItem);
					}
					
					LiveDataThread.sleep(REFRESH_INTERVAL);
				}
			} catch (InterruptedException e) {
				log.error("LiveDataThread",e);
			}			
		}
		
		public void stopThread()
		{
			flag = false;
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		LiveDataChartPanel ldcPnl = new LiveDataChartPanel();
		
		javax.swing.JFrame window = new javax.swing.JFrame("Testing Graph");
        window.setSize(1000, 700);
		window.setLayout(new FlowLayout());
		window.getContentPane().add(ldcPnl);
        window.setLocation(50, 50);
		window.setVisible(true);	
		
		ldcPnl.resetStation("01");
	}
}
